/*
 * Copyright (C) 2012 Caldia Apps.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.caldia.android.frameworkG1.controller;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import org.caldia.android.frameworkG1.activity.AbstractActivity;
import org.caldia.android.frameworkG1.application.Application;
import org.caldia.android.frameworkG1.application.MessagingController;
import org.caldia.android.frameworkG1.preference.MyPreference;
import org.caldia.android.frameworkG1.preference.PreferenceChangeListener;

import android.app.Activity;
import android.support.v4.app.Fragment;

/**
 * 画面管理をするためのクラス
 *
 * @author k-matsuda@caldia.org
 *
 */
public abstract class ActivityManager implements IActivityManager {

	/**
	 * 遷移先画面なし
	 */
	public static final int INVALID_VALUE = -1;

	/**
	 * デバッグフラグ
	 */
	private static final boolean DEBUG = true;

	/**
	 * アプリケーション
	 */
	private final Application mApplication;

	/**
	 * メッセージングコントローラ
	 */
	private MessagingController mController;

	/**
	 * プリファレンス
	 */
	private MyPreference mPreference;

	/**
	 * アクティビティマップ<アクティビティ番号、アクティビティクラス名>
	 */
	private final ConcurrentHashMap<Integer, String> mActivityMap = new ConcurrentHashMap<Integer, String>();

	/**
	 * アクティビティマップ＜アクティビティクラス名、アクティビティ番号>
	 */
	private final ConcurrentHashMap<String, Integer> mActivityClsMap = new ConcurrentHashMap<String, Integer>();

	/**
	 * フラグメントマップ<フラグメント番号、フラグメントクラス名>
	 */
	private final ConcurrentHashMap<Integer, String> mFragmentMap = new ConcurrentHashMap<Integer, String>();

	/**
	 * フラグメントマップ＜フラグメントクラス名、フラグメント番号>
	 */
	private final ConcurrentHashMap<String, Integer> mFragmentClsMap = new ConcurrentHashMap<String, Integer>();

	/**
	 * メッセージリスナーセット
	 */
	private final Set<ControllerListener> mListeners = new CopyOnWriteArraySet<ControllerListener>();

	/**
	 * コンストラクタ
	 *
	 * @param application
	 *            アプリケーション
	 */
	protected ActivityManager(final Application application, final int logNo) {

		// アプリケーション
		mApplication = application;

		log(logNo, "**** コンストラクタ ****", null);

	}

	@Override
	public int getActivityNo(final Class<? extends Activity> cls,
			final int logNo) {

		// クラス名
		final String clsName = cls.getName();

		// アクティビティ番号を取得
		final Integer no = mActivityClsMap.get(clsName);

		if (no != null && 0 <= no) {
			// アクティビティ番号が取得できた
			return no;
		}

		// アクティビティ番号が取得できなかった
		return INVALID_VALUE;
	}

	@Override
	public int getFragmentNo(final Class<? extends Fragment> cls,
			final int logNo) {

		// クラス名
		final String clsName = cls.getName();

		// フラグメント番号
		final Integer no = mFragmentClsMap.get(clsName);

		if (no != null && 0 <= no) {
			// アクティビティ番号が取得できた
			return no;
		}

		// アクティビティ番号が取得できなかった
		return INVALID_VALUE;
	}

	@Override
	public void setMode(final int modeNo, final int logNo) {

		// コマンド
		final Runnable command = new Runnable() {

			@Override
			public void run() {

				log(logNo, "モード番号保存", null);

				// プリファレンスに保存
				getPreference(logNo).putInt("ActivityManagerModeNo", modeNo,
						new PreferenceChangeListener());
			}
		};

		// コマンド送信
		getMessagingController(logNo).putCommand("setMode", command,
				new ControllerListener());

	}

	@Override
	public int getMode(final int logNo) {

		final int modeNo = getPreference(logNo).getInt("ActivityManagerModeNo",
				INVALID_VALUE);

		log(logNo, "モード番号取得 : " + modeNo, null);

		return modeNo;
	}

	@Override
	public void addActivity(final int activityNo,
			final Class<? extends Activity> cls, final int logNo) {

		// コマンド
		final Runnable runnable = new Runnable() {

			@Override
			public void run() {
				// クラス名
				final String clsName = cls.getName();

				// アクティビティマップに追加
				mActivityClsMap.putIfAbsent(clsName, activityNo);
				mActivityMap.putIfAbsent(activityNo, clsName);

				log(logNo, "アクティビティ追加 : " + clsName + " << " + activityNo, null);

			}
		};

		// コマンド送信
		getMessagingController(logNo).putCommand("addActivity", runnable,
				new ControllerListener());

	}

	@Override
	public void addFragment(final int fragmentNo,
			final Class<? extends Fragment> cls, final int logNo) {

		// コマンド
		final Runnable runnable = new Runnable() {

			@Override
			public void run() {
				// クラス名
				final String clsName = cls.getName();

				// フラグメントマップに追加
				mFragmentClsMap.putIfAbsent(clsName, fragmentNo);
				mFragmentMap.putIfAbsent(fragmentNo, clsName);

				log(logNo, "フラグメント追加 : " + clsName + " << " + fragmentNo, null);

			}
		};

		// コマンド送信
		getMessagingController(logNo).putCommand("addFragment", runnable,
				new ControllerListener());

	}

	@Override
	public void setFragmentActivity(
			final Class<? extends AbstractActivity> activity, final int logNo) {

		final Runnable runnable = new Runnable() {

			@Override
			public void run() {

				getPreference(logNo).putString(
						"activityManagerFragmentActivity", activity.getName(),
						new PreferenceChangeListener());

				log(logNo, "フラグメントアクティビティを保存" + activity.getName(), null);

			}
		};

		// コマンド送信
		getMessagingController(logNo).putCommand("setFragmentActivity",
				runnable, new ControllerListener());
	}

	@Override
	public String getFragmentActivityName(final int logNo) {

		return getPreference(logNo).getString(
				"activityManagerFragmentActivity", null);

	}

	@Override
	public MessagingController getMessagingController(final int logNo) {

		log(logNo, "メッセージコントローラ取得", null);

		if (mController == null) {
			mController = MessagingController.getInstance();
		}
		return mController;
	}

	@Override
	public MyPreference getPreference(final int logNo) {

		if (mPreference == null) {
			mPreference = MyPreference.getInstance(getApplication(logNo));
		}
		return mPreference;

	}

	@Override
	public String getActivityName(final int activityNo, final int logNo) {

		return mActivityMap.get(activityNo);

	}

	@Override
	public String getFragmentName(final int fragmentNo, final int logNo) {

		return mFragmentMap.get(fragmentNo);

	}

	@Override
	public Application getApplication(final int logNo) {

		log(logNo, "アプリケーション取得", null);

		return mApplication;
	}

	@Override
	public Set<ControllerListener> getMessagingListeners(final int logNo) {
		return getMessagingListeners(null, logNo);
	}

	@Override
	public Set<ControllerListener> getMessagingListeners(
			final ControllerListener l, final int logNo) {

		log(logNo, "メッセージングリスナー取得", null);

		// メッセージングリスナーをコピー
		final HashSet<ControllerListener> set = new HashSet<ControllerListener>(
				mListeners);

		if (l != null) {
			log(logNo, "メッセージングリスナー取得（追加あり）", null);
			// リスナーを追加
			set.add(l);
		}

		return set;
	}

	@Override
	public void addMessagingListener(final ControllerListener listener) {

		log(INVALID_VALUE, "メッセージリスナー追加", null);

		if (listener != null) {
			mListeners.add(listener);
		}

	}

	@Override
	public void removeMessagingListener(final ControllerListener l) {

		// コマンド
		final Runnable runnable = new Runnable() {

			@Override
			public void run() {
				log(INVALID_VALUE, "メッセージリスナー削除", null);
				mListeners.remove(l);
			}
		};

		// コマンド送信
		getMessagingController(INVALID_VALUE).putCommand(
				"removeMessagingListener", runnable, new ControllerListener());

	}

	/**
	 * ログ出力
	 *
	 * @param logNo
	 *            トラッキング用番号
	 * @param text
	 *            メッセージ
	 * @param tr
	 *            例外
	 */
	private void log(final int logNo, final String text, final Throwable tr) {
		if (DEBUG) {
			if (tr == null) {
//				CLog.dt(this, logNo, text);
			} else {
//				CLog.d(this, logNo, text, tr);
			}

		}
	}

	@Override
	public int getNo(final Object obj) {

		if (obj == null) {
			return INVALID_VALUE;
		}

		// クラス名
		final String name = obj.getClass().getName();

		final Integer activityNo = mActivityClsMap.get(name);

		if (activityNo != null) {
			return activityNo;
		}

		final Integer fragmentNo = mFragmentClsMap.get(name);

		if (fragmentNo != null) {
			return fragmentNo;
		}

		return INVALID_VALUE;
	}

}
